package fem2.gui.pm;

import static java.lang.Math.floor;
import fem2.Model;
import fem2.gui.pm.extr.Extractor2D;
import fem2.gui.pm.extr.ExtractorUtilities;
import fem2.gui.src.HofemSolidElementsSource;
import fem2.gui.src.HofemSource;
import inf.vtk.pm.SolidElementsDP;

import java.beans.PropertyChangeEvent;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import vtk.vtkDataArray;
import vtk.vtkPolyData;

public class HofemSolidElementsDP extends HofemDataProvider implements
		SolidElementsDP {

	private HofemSolidElementsSource source_ = new HofemSolidElementsSource();
	private String field_;
	private Map<String, Extractor2D> extractors_ = new TreeMap<String, Extractor2D>();

	public List<String> getProvidedScalarData() {
		return new LinkedList<String>(extractors_.keySet());
	}

	public List<String> getProvidedVectorData() {
		throw new Error();
	}

	public String getScalarDataName() {
		return field_;
	}

	public double[] getScalarDataRange() {
		source_.Update();

		vtkDataArray data = source_.GetPolyDataOutput().GetPointData()
				.GetScalars();
		int n = data.GetSize();
		double[] vs = new double[n];

		for (int i = 0; i < n; i++) {
			vs[i] = data.GetTuple1(i);
		}
		Arrays.sort(vs);
		int offset = (int) (floor(0.05 * n));
		return new double[] { vs[offset], vs[n - offset - 1] };
	}

	public vtkPolyData getSolidElementsPD() {
		return source_.GetPolyDataOutput();
	}

	@Override
	protected List<HofemSource> getSources() {
		List<HofemSource> l = new LinkedList<HofemSource>();

		l.add(source_);
		return l;
	}

	public String getVectorDataName() {
		throw new Error();
	}

	public double[] getVectorDataNormRange() {
		source_.Update();

		return source_.getVectorDataNormRange();
	}

	@Override
	protected void modelChange(PropertyChangeEvent evt) {
		updateFields();
	}

	public boolean providesData() {
		Model m = getCastedModel().getModel();

		return m != null && m.getMesh().countFaces() > 0;
	}

	public boolean providesScalarElementData() {
		return providesData() && getCastedModel().solutionAvailable();
	}

	public boolean providesVectorElementData() {
		return providesData() && getCastedModel().solutionAvailable();
	}

	public void setResolution(int r) {
		source_.setResolution(r);
	}

	public void setScalarDataName(String field) {
		if (!field.equals(field_)) {
			if (!extractors_.containsKey(field)) {
				throw new IllegalArgumentException("No such field: " + field);
			}
			field_ = field;
			source_.setExtractor(extractors_.get(field));
			firePropertyChange(PROP_DATA_NAME, field_);
		}
	}

	public void setVectorDataName(String field) {
		throw new Error();
	}

	@Override
	protected void solutionChanged(PropertyChangeEvent p) {
		firePropertyChange(PROP_DATA_RANGE, null);
		firePropertyChange(PROP_PROVIDES_DATA, true);
	}

	private void updateFields() {
		// extractors_ =
		// ExtractorUtilities.getExtractors2D(getCastedModel().getModel());
		//
		// if (extractors_.size() > 0) {
		// String name = extractors_.keySet().iterator().next();
		// setScalarDataName(name);
		// firePropertyChange(PROP_DATA_NAME, name);
		// }
	}
}
